package com.we.wfc.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.we.wfc.common.base.BaseException;
import com.we.wfc.common.base.BaseJpaEntity;
import com.we.wfc.common.enums.ReturnCode;
import org.apache.poi.ss.usermodel.Sheet;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.time.OffsetDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * excel工具类，对hutool进行excel工具类 再次封装
 *
 * @author zhangby
 * @date 2018/4/8 下午1:18
 */
public class ExcelUtil {

    /**
     * excel 导出
     *
     * @param fileName           导出文件名
     * @param title              excel 标题名
     * @param list               导出数据
     * @param key                excel导出的列名，已经对应的key -> "name:姓名,age:年龄"
     * @param columnWidth        列宽
     * @param defaultColumnWidth 默认列宽
     * @param function           <参数1：colums(record 列表)，参数2：record，参数3：列表结果>
     */
    public static void export4Map(HttpServletResponse response, String fileName, String title, String key,
                                  String columnWidth, Integer defaultColumnWidth, List<Dict> list, BiFunction<Dict, String, String> function) {
        // 验证导出列表
        CommonUtil.emptyStr(key).orElseThrow(() -> new BaseException(ReturnCode.EXCEL_DOWNLOAD_FAILED));
        // 格式化导出列名
        List<String> keyList = Lists.newArrayList(key.split(","));
        List<String> colums = keyList.stream().map(item -> item.split(":")[0]).collect(Collectors.toList());
        List<String> header = keyList.stream().map(item -> item.split(":")[1]).collect(Collectors.toList());

        /** 构建数据集 */
        List<List<String>> excelData = Lists.newArrayList();
        // 添加头部信息
        excelData.add(header);
        /** 构建实体数据 */
        list.forEach(item -> {
            List<String> itemList = Lists.newArrayList();
            colums.forEach(columskey -> {
                // 是否需要执行，预处理
                if (ObjectUtil.isNotNull(function)) {
                    itemList.add(function.apply(item, columskey));
                } else {
                    itemList.add(item.getStr(columskey));
                }
            });
            excelData.add(itemList);
        });
        /** 写文件 */
        ExcelWriter writer = cn.hutool.poi.excel.ExcelUtil.getWriter(true);
        /** 判断是否有标题 */
        CommonUtil.emptyStr(title).ifPresent(tit -> writer.merge(keyList.size() - 1, tit));
        /** 设置列宽 */
        Sheet sheet = writer.getSheet();
        if (ObjectUtil.isNotNull(defaultColumnWidth)) {
            for (int i = 0; i < colums.size(); i++) {
                sheet.setColumnWidth(i, defaultColumnWidth);
            }
        }
        if (StrUtil.isNotBlank(columnWidth)) {
            Map<String, Integer> columnWidthMap = CommonUtil.resolve(() ->
                    Lists.newArrayList(columnWidth.split(",")).stream().filter(StrUtil::isNotBlank)
                            .collect(Collectors.toMap(item -> item.split(":")[0], item -> Integer.parseInt(item.split(":")[1]))))
                    .orElse(Maps.newHashMap());
            columnWidthMap.forEach((column, width) -> {
                int indexOf = colums.indexOf(column);
                sheet.setColumnWidth(indexOf, width);
            });
        }
        writer.write(excelData, true);
        ServletOutputStream out = null;
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(CommonUtil.emptyStr(fileName).orElse("excel")) + ".xlsx");
            out = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            new BaseException(ReturnCode.EXCEL_DOWNLOAD_FAILED);
        } finally {
            writer.flush(out, true);
            // 关闭writer，释放内存
            writer.close();
            //此处记得关闭输出Servlet流
            IoUtil.close(out);
        }
    }

    /**
     * excel 导出
     *
     * @param fileName 导出文件名
     * @param title    excel 标题名
     * @param list     导出数据
     * @param key      excel导出的列名，已经对应的key -> "name:姓名,age:年龄"
     * @param function <参数1：colums(record 列表)，参数2：record，参数3：列表结果>
     */
    public static <T extends BaseJpaEntity> void export(HttpServletResponse response, String fileName, String title, String key,
                                                        String columnWidth, Integer defaultColumnWidth,
                                                        List<T> list, BiFunction<T, String, String> function) {
        // 验证导出列表
        CommonUtil.emptyStr(key).orElseThrow(() -> new BaseException(ReturnCode.EXCEL_DOWNLOAD_FAILED));
        // 格式化导出列名
        List<String> keyList = Lists.newArrayList(key.split(","));
        // 格式化导出列名
        List<String> colums = keyList.stream().map(item -> item.split(":")[0]).collect(Collectors.toList());

        /** 格式化excel数据 */
        List<Dict> excelDataList = Lists.newArrayList();
        list.forEach(t -> {
            Dict excelData = Dict.create();
            Class<? extends BaseJpaEntity> clazz = t.getClass();
            colums.forEach(columskey -> {
                // 是否需要执行，预处理
                if (ObjectUtil.isNotNull(function)) {
                    String apply = function.apply(t, columskey);
                    excelData.set(columskey, apply);
                } else {
                    String value = reflectMethodGet(t, columskey, String.class);
                    excelData.set(columskey, value);
                }
            });
            excelDataList.add(excelData);
        });
        /** 写文件 */
        ExcelWriter writer = cn.hutool.poi.excel.ExcelUtil.getWriter(true);
        /** 判断是否有标题 */
        CommonUtil.emptyStr(title).ifPresent(tit -> writer.merge(keyList.size() - 1, tit));
        /** 替换列名 */
        keyList.forEach(keyStr -> writer.addHeaderAlias(keyStr.split(":")[0], keyStr.split(":")[1]));
        /** 设置列宽 */
        Sheet sheet = writer.getSheet();
        if (ObjectUtil.isNotNull(defaultColumnWidth)) {
            for (int i = 0; i < colums.size(); i++) {
                sheet.setColumnWidth(i, defaultColumnWidth);
            }
        }
        if (StrUtil.isNotBlank(columnWidth)) {
            Map<String, Integer> columnWidthMap = CommonUtil.resolve(() ->
                    Lists.newArrayList(columnWidth.split(",")).stream().filter(StrUtil::isNotBlank)
                            .collect(Collectors.toMap(item -> item.split(":")[0], item -> Integer.parseInt(item.split(":")[1]))))
                    .orElse(Maps.newHashMap());
            columnWidthMap.forEach((column, width) -> {
                int indexOf = colums.indexOf(column);
                sheet.setColumnWidth(indexOf, width);
            });
        }
        writer.write(excelDataList, true);
        /** 修改列名 */
        ServletOutputStream out = null;
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(CommonUtil.emptyStr(fileName).orElse("excel")) + ".xlsx");
            out = response.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            new BaseException(ReturnCode.EXCEL_DOWNLOAD_FAILED);
        } finally {
            writer.flush(out, true);
            // 关闭writer，释放内存
            writer.close();
            //此处记得关闭输出Servlet流
            IoUtil.close(out);
        }
    }

    /**
     * 执行get 反射方法
     */
    public static <T extends BaseJpaEntity, R> Object reflectMethodGet(T t, String columskey) {
        Method method = ReflectUtil.getMethod(t.getClass(),
                "get" + columskey.substring(0, 1).toUpperCase() + columskey.substring(1, columskey.length()));
        Object invoke = null;
        try {
            invoke = method.invoke(t);
            // 判断类型，如果为日期类型单独处理
            if (OffsetDateTime.class.equals(method.getReturnType())) {
                return OffsetDateTimeUtils.formatDateTime((OffsetDateTime) invoke, DatePattern.NORM_DATETIME_PATTERN);
            }
            if (Date.class.equals(method.getReturnType())) {
                return DateUtil.formatDateTime((Date) invoke);
            }
        } catch (Exception e) {
        }
        return invoke;
    }

    /**
     * 执行get 反射方法
     */
    public static <T extends BaseJpaEntity, R> R reflectMethodGet(T t, String columskey, Class<R> returnClass) {
        return Convert.convert(returnClass, reflectMethodGet(t, columskey));
    }
}
